function [dtSV] = svcodephasetimeoffset(tSV, clkErrPar, TGD, ephem, cfg, cst) %#codegen
%SVCODEPHASETIMEOFFSET 计算卫星测距码相位时间偏移
%
% Input Arguments
% # tSV: 标量，为消息发送时刻的卫星测距码相位时间，单位s
% # clkErrPar: 结构体，钟差参数，包含以下域：
%     tOC: 标量，参考时间，单位：秒
%     af0: 标量，常数项，单位：秒
%     af1: 标量，一阶项，单位：秒/秒
%     af2: 标量，二阶项，单位：秒/秒^2
% # TGD: 长度为2的向量，对于GPS系统，元素1为L1频点的TGD，对于北斗系统，元素1、2分别为B1、B2频点的TGD
% # ephem: 结构体，星历，包含以下域：
%     ASqrt: 标量，长半轴的平方根，单位：m^0.5
%     e: 标量，偏心率，值域：0~0.03，单位：无量纲
%     E：标量，信号发射时刻的偏近点角，单位：rad
% # cfg: 结构体，配置量，包含以下域：
%     sysType: GNSSSysType枚举类型，GNSS系统类型
%     SVChannel: int8标量，取值0-5，对于GPS系统，0、1、2、5分别表示L1/L2双频、L1、L2、L5单频，对于北斗系统，0、1、2、3分别表示B1/B2双频、B1、B2、B3单频
% # cst: 结构体，常量，包含以下域：
%     f: 长度为5的向量，对于GPS系统，第1、2、5元素分别为L1、L2、L5频点的频率，对于北斗系统，第1、2、3元素分别为B1、B2、B3频点的频率
%     F: 标量，相对论效应常数，单位：s/m^0.5
%
% Output Arguments
% # dtSV: 标量，卫星测距码相位时间偏移，单位s
%
% Assumptions and Limitations
% # 本函数仅支持GPS及北斗系统
%
% References:
% # 《应用导航算法工程基础》“卫星时钟校正”

% 修正卫星时钟相对论效应项
dtR = cst.F * ephem.e * ephem.ASqrt * sin(ephem.E);

% 卫星测距码相位时间偏移
tDiff = accountforweekcrossover(tSV-clkErrPar.tOC);
dtSV = clkErrPar.af0 + clkErrPar.af1*(tDiff) + clkErrPar.af2*(tDiff)^2 + dtR;

% 星上设备时延校正
switch cfg.sysType
    case GNSSSysType.GPS
        switch cfg.SVChannel
            case int8(0) % L1/L2双频
                gamma = 0;
            case int8(1) % L1
                gamma = 1;
            case int8(2) % L2
                gamma = (cst.f(1)/cst.f(2))^2;
            case int8(5) % L5
                gamma = (cst.f(1)/cst.f(5))^2;
            otherwise
                error('svcodephasetimeoffset:unknownsvchannel', 'GPS SV channel is not L1/L2, L1, L2 or L5.');
        end
        dtSV = dtSV - gamma*TGD(1);
    case {GNSSSysType.GLONASS, GNSSSysType.Galileo}
        error('svcodephasetimeoffset:unsupportedgnsssys', 'GLONASS and Galileo systems are not supported.');
    case GNSSSysType.BeiDou
        switch cfg.SVChannel
            case int8(0) % B1/B2双频
                effectiveTGD = (cst.f(2)^2*TGD(2)-cst.f(1)^2*TGD(1)) / (cst.f(2)^2-cst.f(1)^2);
            case int8(1) % B1
                effectiveTGD = TGD(1);
            case int8(2) % B2
                effectiveTGD = TGD(2);
            case int8(3) % B3
                effectiveTGD = 0;
            otherwise
                error('svcodephasetimeoffset:unknownsvchannel', 'BeiDou SV channel is not B1/B2, B1, B2 or B3.');
        end
        dtSV = dtSV - effectiveTGD;
    otherwise
        error('svcodephasetimeoffset:unknowngnsssys', 'GNSS system is not GPS, GLONASS, Galileo or BeiDou.');
end
end